﻿/*******************************************************************************************************************************************
Modification History:
********************************************************************************************************************************************
Date		Author		    Description
********************************************************************************************************************************************
05/08/2011	Brian Kuhn	    Created SpecificationExtension class
*******************************************************************************************************************************************/
using System;
using System.Linq;
using System.Linq.Expressions;

namespace Concise
{
    /// <summary>
    /// Provides a set of methods that extend the <see cref="ISpecification{T}"/> interface.
    /// </summary>
    public static class SpecificationExtension
    {
        //=======================================================================================================
        //	Extension Methods
        //=======================================================================================================
        #region And<T>(this ISpecification<T> specification, ISpecification<T> left)
        /// <summary>
        /// Returns a new specification by performing a boolean logical XOR of this specification with another specification.
        /// </summary>
        /// <typeparam name="T">The type the specification defines a set of criteria for.</typeparam>
        /// <param name="specification">The <see cref="Specification{T}"/> the <see cref="And{T}"/> method operates on.</param>
        /// <param name="left">The <see cref="Specification{T}"/> to add to this specification using a boolean logical XOR.</param>
        /// <returns>The combined <see cref="Specification{T}"/> instance.</returns>
        public static ISpecification<T> And<T>(this ISpecification<T> specification, ISpecification<T> left)
        {
            var rightInvoke         = Expression.Invoke(specification.Predicate, left.Predicate.Parameters.Cast<Expression>());
            var binaryExpression    = Expression.MakeBinary(ExpressionType.AndAlso, left.Predicate.Body, rightInvoke);

            return new Specification<T>(Expression.Lambda<Func<T, bool>>(binaryExpression, left.Predicate.Parameters));
        }
        #endregion

        #region Or<T>(this ISpecification<T> specification, ISpecification<T> left)
        /// <summary>
        /// Returns a new specification by performing a boolean logical OR of this specification with another specification.
        /// </summary>
        /// <typeparam name="T">The type the specification defines a set of criteria for.</typeparam>
        /// <param name="specification">The <see cref="Specification{T}"/> the <see cref="Or{T}"/> method operates on.</param>
        /// <param name="left">The <see cref="Specification{T}"/> to add to this specification using a boolean logical OR.</param>
        /// <returns>The combined <see cref="Specification{T}"/> instance.</returns>
        public static ISpecification<T> Or<T>(this ISpecification<T> specification, ISpecification<T> left)
        {
            var rightInvoke         = Expression.Invoke(specification.Predicate, left.Predicate.Parameters.Cast<Expression>());
            var binaryExpression    = Expression.MakeBinary(ExpressionType.OrElse, left.Predicate.Body, rightInvoke);

            return new Specification<T>(Expression.Lambda<Func<T, bool>>(binaryExpression, left.Predicate.Parameters));
        }
        #endregion
    }
}
